#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do{p += (strlen(p)-1);while(*p!='/') p--;}while(0)

char cwd[SIZE*2];
char* gArgv[NUM];
int lastcode=0;

void Die()
{
  exit(1);
}
const char* GetHome()
{
  const char* home=getenv("HOME");
  if(home==NULL) return "/";
  return home;
}
const char* GetUserName()
{
  const char* username=getenv("USER");

  if(username==NULL) return "None";
  return username;
}
const char* GetHostName()
{
  const char* hostname=getenv("HOSTNAME");
  if(hostname==NULL) return "None";
  return hostname;
}
const char* GetCwd()
{
  const char* cwd=getenv("PWD");
  if(cwd==NULL) return "None";
  return cwd;
}
//commandline:output
void MakeCommandLineAndPrint()
{
  char line[SIZE];
  const char* username=GetUserName();
  const char* hostname=GetHostName();
  const char* cwd=GetCwd();
  SkipPath(cwd);
  snprintf(line,sizeof(line),"[%s@%s %s]> ",username,hostname,strlen(cwd)==1?"/":cwd+1);
  printf("%s",line);
  fflush(stdout);
}

int GetUserCommand(char command[] ,size_t n)
{
  char* s=fgets(command,n,stdin);
  if(s==NULL) return -1;//暂时设置成1
  command[strlen(command)-1]=ZERO;
  return strlen(command);
}
//#define NUM 32

void SplitCommand(char command[],size_t n)
{
  (void)n;
  // "ls -a -l -n" -> "ls""-a""-l""-n"
  gArgv[0]=strtok(command,SEP);
  int index=1;
  while((gArgv[index++]=strtok(NULL,SEP)));//done 故意写成=，表示先赋值，再判断，分割之后，strtok会返回NULL，刚好让gArgv最后一个元素是NULL，并且while判断结束
  
}

void ExecuteCommand()
{
  pid_t id=fork();
  if(id<0) Die();
  else if(id==0)
  {
    //child
    execvp(gArgv[0],gArgv);
    exit(errno);
  }
  else
  {
    //father
    int status = 0;
    pid_t rid=waitpid(id,&status,0);
    if(rid>0)
    {
      //success
      lastcode=WEXITSTATUS(status);
      if(lastcode != 0) printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
    }
  }
}
void Cd()
{
  const char* path=gArgv[1];
  if(path==NULL) path=GetHome();
  //path 一定存在
  chdir(path);
  
  //刷新环境变量
  char temp[SIZE*2];
  getcwd(temp,sizeof(temp));
  snprintf(cwd,sizeof(cwd),"PWD=%s",temp);
  putenv(cwd);
}
int CheckBulid()
{
  int yes=0;
  const char* enter_cmd=gArgv[0];
  if(strcmp(enter_cmd,"cd")==0)
  {
    yes=1;
    Cd();
  }
  else if(strcmp(enter_cmd,"echo")==0&&strcmp(gArgv[1],"$?")==0)
  {
    yes=1;
    printf("%d\n",lastcode);
    lastcode=0;
  }
  return yes;
}
int main()
{
  int quit = 0;
  while(!quit)
  {
    //1.我们需要自己输出一个命令行
    MakeCommandLineAndPrint();
    
    //2.获取用户命令字符串
    char usercommand[SIZE];
    int n=GetUserCommand(usercommand,sizeof(usercommand));
    (void)n;

    //printf("echo : %s\n",usercommand);
    
    //3.命令行字符串分割
    SplitCommand(usercommand,sizeof(usercommand));
    //printf("echo : %s\n",usercommand);
    //for(int i=0;gArgv[i];i++)
    //{
    //  printf("gArgv[%d]: %s\n",i,gArgv[i]);
    //}
    //4.检查命令是否是内建命令
    
    n = CheckBulid();
    if(n) continue;
    //n.执行命令
    ExecuteCommand();
  }
  return 0;
}
